/**
* Copyright (C) 2008 Happy Fish / YuQing
*
* FastDFS may be copied only under the terms of the GNU General
* Public License V3, which may be found in the FastDFS source kit.
* Please visit the FastDFS Home Page http://www.csource.org/ for more detail.
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <event.h>
#include "shared_func.h"
#include "logger.h"
#include "global.h"
#include "global.h"
#include "ini_file_reader.h"
#include "sockopt.h"
#include "sched_thread.h"
#include "queue.h"
#include "func.h"
#include "client_io.h"
#include "server_io.h"

static ScheduleArray scheduleArray;
static pthread_t schedule_tid;

static int fnio_init_schedule();
static void sigQuitHandler(int sig);
static void sigHupHandler(int sig);
static void sigUsrHandler(int sig);

int main(int argc, char *argv[])
{
	char *conf_filename;
	char bind_addr[IP_ADDRESS_SIZE];
	
	int result;
	int sock;
	struct sigaction act;
	
	memset(bind_addr, 0, sizeof(bind_addr));
	if (argc < 2)
	{
		fprintf(stderr, "Usage: %s <config_file>\n", argv[0]);
		return 1;
	}

	conf_filename = argv[1];
	if ((result=fnio_func_init(conf_filename, bind_addr, \
		sizeof(bind_addr))) != 0)
	{
		return result;
	}

	daemon_init(true);
	umask(0);

	if (g_server_mode == FNIO_SERVER_MODE_CLIENT)
	{
		sock = socketServer(bind_addr, g_proxy_client_port, &result);
	}
	else
	{
		sock = socketServer(bind_addr, g_proxy_server_port, &result);
	}
	if (sock < 0)
	{
		return result;
	}

	if ((result=tcpsetnonblockopt(sock, g_network_timeout)) != 0)
	{
		return result;
	}

	memset(&act, 0, sizeof(act));
	sigemptyset(&act.sa_mask);

	act.sa_handler = sigUsrHandler;
	if(sigaction(SIGUSR1, &act, NULL) < 0 || \
		sigaction(SIGUSR2, &act, NULL) < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call sigaction fail, errno: %d, error info: %s", \
			__LINE__, errno, strerror(errno));
		return errno;
	}

	act.sa_handler = sigHupHandler;
	if(sigaction(SIGHUP, &act, NULL) < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call sigaction fail, errno: %d, error info: %s", \
			__LINE__, errno, strerror(errno));
		return errno;
	}
	
	act.sa_handler = SIG_IGN;
	if(sigaction(SIGPIPE, &act, NULL) < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call sigaction fail, errno: %d, error info: %s", \
			__LINE__, errno, strerror(errno));
		return errno;
	}

	act.sa_handler = sigQuitHandler;
	if(sigaction(SIGINT, &act, NULL) < 0 || \
		sigaction(SIGTERM, &act, NULL) < 0 || \
		sigaction(SIGABRT, &act, NULL) < 0 || \
		/*sigaction(SIGSEGV, &act, NULL) < 0 || \ */
		sigaction(SIGQUIT, &act, NULL) < 0)
	{
		logError("file: "__FILE__", line: %d, " \
			"call sigaction fail, errno: %d, error info: %s", \
			__LINE__, errno, strerror(errno));
		return errno;
	}

	if ((result=fnio_queue_init()) != 0)
	{
		g_continue_flag = false;
		return result;
	}

	g_event_base = event_base_new();
	if (g_event_base == NULL)
	{
		logCrit("file: "__FILE__", line: %d, " \
			"event_base_new fail.", __LINE__);
		return ENOMEM;
	}

	if ((result=server_io_init()) != 0)
	{
		return result;
	}

	if ((result=client_io_init(sock)) != 0)
	{
		return result;
	}

	if ((result=fnio_init_schedule()) != 0)
	{
		g_continue_flag = false;
		return result;
	}

	//log_set_cache(true);
	while (g_continue_flag)
	{
		event_base_loop(g_event_base, 0);
	}

	event_base_free(g_event_base);


	fnio_queue_destroy();

	close(sock);

	while (g_schedule_flag) //waiting for schedule thread exit
	{
		sleep(1);
	}

	logInfo("exit nomally.\n");
	log_destory();
	
	return 0;
}

static void sigQuitHandler(int sig)
{
	if (g_continue_flag)
	{
		g_continue_flag = false;
		if (g_event_base != NULL)
		{
			struct timeval tv;
			tv.tv_sec = 1;
			tv.tv_usec = 0;
			event_base_loopexit(g_event_base, &tv);
		}

		pthread_kill(schedule_tid, SIGINT);
		//fnio_terminate();
		logCrit("file: "__FILE__", line: %d, " \
			"catch signal %d, program exiting...", \
			__LINE__, sig);

		/*
		//printf("free queue count: %d, recv queue count: %d, " \
			"work queue count=%d, send queue count=%d\n", \
			free_queue_count(), recv_queue_count(),  \
			work_queue_count(), send_queue_count());
		fflush(stdout);
		*/
	}
}

static void sigHupHandler(int sig)
{
}

static void sigUsrHandler(int sig)
{
	/*
	logInfo("current thread count=%d, " \
		"mo count=%d, success count=%d", g_thread_count, \
		nMoCount, nSuccMoCount);
	*/
}

static int fnio_init_schedule()
{
	int entry_count;
	ScheduleEntry *pScheduleEntry;

	entry_count = 1;

	scheduleArray.entries = (ScheduleEntry *)malloc( \
				sizeof(ScheduleEntry) * entry_count);
	if (scheduleArray.entries == NULL)
	{
		logError("file: "__FILE__", line: %d, " \
			"malloc %d bytes fail, " \
			"errno: %d, error info: %s", \
			__LINE__, sizeof(ScheduleEntry) * entry_count, \
			errno, strerror(errno));
		return errno != 0 ? errno : ENOMEM;
	}

	pScheduleEntry = scheduleArray.entries;
	scheduleArray.count = entry_count;

	memset(pScheduleEntry, 0, sizeof(ScheduleEntry) * entry_count);
	pScheduleEntry->id = pScheduleEntry - scheduleArray.entries + 1;
	pScheduleEntry->time_base.hour = TIME_NONE;
	pScheduleEntry->time_base.minute = TIME_NONE;
	pScheduleEntry->interval = g_sync_log_buff_interval;
	pScheduleEntry->task_func = log_sync_func;
	pScheduleEntry->func_args = NULL;
	pScheduleEntry++;

	return sched_start(&scheduleArray, &schedule_tid);
}

